package com.salt.core.oss.minio;

import com.salt.core.oss.OssTemplate;
import com.salt.core.oss.minio.constant.Base64Constant;
import com.salt.core.oss.minio.enums.PolicyType;
import com.salt.core.oss.minio.utils.ImageUtil;
import com.salt.core.oss.model.OssFile;
import com.salt.core.oss.model.SaltFile;
import com.salt.core.oss.props.OssProperties;
import com.salt.core.oss.rule.OssRule;
import com.salt.core.tool.utils.StringPool;
import com.salt.core.tool.utils.StringUtil;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.Result;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Optional;

/**
 * @Description Minio Oss 模版类
 * @Author salt
 * @Date 2020-11-09
 * @Version 1.0.1
 */
@AllArgsConstructor
public class MinioTemplate implements OssTemplate {

    private final MinioClient client;

    private final OssRule ossRule;

    private final OssProperties ossProperties;

    @SneakyThrows
    @Override
    public void makeBucket(String bucketName) {
        if (!client.bucketExists(getBucketName(bucketName))) {
            client.makeBucket(getBucketName(bucketName));
            client.setBucketPolicy(
                    getBucketName(bucketName),getPolicyType(getBucketName(bucketName), PolicyType.READ));
        }
    }

    /**
     * 获取存储桶
     * @param bucketName 存储桶名称
     * @return
     */
    @SneakyThrows
    public Bucket getBucket(String bucketName) {
        Optional<Bucket> bucketOptional = client.listBuckets().stream().filter(bucket -> bucket.equals(bucketName)).findFirst();
        return bucketOptional.orElse(null);
    }

    @SneakyThrows
    @Override
    public void removeBucket(String bucketName) {
        client.removeBucket(getBucketName(bucketName));
    }

    @SneakyThrows
    @Override
    public boolean bucketExists(String bucketName) {
        return client.bucketExists(getBucketName());
    }

    @SneakyThrows
    @Override
    public void copyFile(String bucketName, String fileName, String destBucketName) {
        client.copyObject(bucketName, fileName, destBucketName);
    }

    @SneakyThrows
    @Override
    public void copyFile(String bucketName, String fileName, String destBucketName, String destFileName) {
        client.copyObject(bucketName, fileName, destBucketName, destFileName);
    }

    @Override
    public OssFile statFile(String fileName) {
        return statFile(ossProperties.getBucketName(), fileName);
    }

    @SneakyThrows
    @Override
    public OssFile statFile(String bucketName, String fileName) {
        ObjectStat stat = client.statObject(bucketName, fileName);
        OssFile ossFile = new OssFile();
        ossFile.setPutTime(stat.createdTime());
        ossFile.setContentType(stat.contentType());
        ossFile.setLength(stat.length());
        ossFile.setHash(String.valueOf(stat.hashCode()));
        ossFile.setName(StringUtil.isEmpty(stat.bucketName())?fileName : stat.bucketName());
        ossFile.setLink(fileLink(ossFile.getName()));
        return ossFile;
    }

    @Override
    public String filePath(String fileName) {
        return getBucketName().concat(StringPool.SLASH).concat(fileName);
    }

    @Override
    public String filePath(String bucketName, String fileName) {
        return getBucketName(bucketName).concat(StringPool.SLASH).concat(fileName);
    }

    @Override
    public String fileLink(String fileName) {
        return ossProperties.getEndpoint().concat(StringPool.SLASH).
                concat(getBucketName()).concat(StringPool.SLASH).
                concat(fileName);
    }

    @Override
    public String fileLink(String bucketName, String fileName) {
        return ossProperties.getEndpoint().concat(StringPool.SLASH).
                concat(getBucketName(bucketName)).concat(StringPool.SLASH).
                concat(fileName);
    }

    @Override
    public String outerFileLink(String fileName) {
        return ossProperties.getOuterEndpoint().concat(StringPool.SLASH).
                concat(getBucketName()).concat(StringPool.SLASH).
                concat(fileName);
    }

    @Override
    public String outerFileLink(String bucketName, String fileName) {
        return ossProperties.getOuterEndpoint().concat(StringPool.SLASH).
                concat(getBucketName(bucketName)).concat(StringPool.SLASH).
                concat(fileName);
    }

    @Override
    public SaltFile putFile(MultipartFile file) {
        return putFile(file, file.getOriginalFilename(), ossProperties.getBucketName());
    }

    @Override
    public SaltFile putFile(MultipartFile file, String fileName) {
        return putFile(file, fileName, ossProperties.getBucketName());
    }

    @SneakyThrows
    @Override
    public SaltFile putFile(MultipartFile file, String fileName, String bucketName) {
        return putFile(bucketName, file.getOriginalFilename(), file.getInputStream());
    }

    @Override
    public SaltFile putFile(String fileName, InputStream stream) {
        return putFile(ossProperties.getBucketName(), fileName, stream);
    }

    @Override
    public SaltFile putFile(String bucketName, String fileName, InputStream stream) {
        return putFile(bucketName, fileName, stream, "application/octet-stream");
    }

    /**
     * 上传文件
     * @param bucketName 存储桶名称
     * @param fileName 存储桶文件名称
     * @param stream 文件流
     * @param contentType 类型
     * @return
     */
    @SneakyThrows
    public SaltFile putFile(String bucketName, String fileName, InputStream stream, String contentType) {
        makeBucket(bucketName);
        String originalFilename = fileName;
        fileName = getFileName(originalFilename);
        client.putObject(getBucketName(bucketName), fileName, stream, (long) stream.available(), null, null, contentType);
        SaltFile saltFile = new SaltFile();
        saltFile.setName(fileName);
        saltFile.setDomain(getOssHost(bucketName));
        saltFile.setOriginalName(originalFilename);
        saltFile.setLink(fileLink(bucketName, fileName));
        saltFile.setOuterLink(outerFileLink(bucketName, fileName));
        return saltFile;
    }

    @Override
    public void removeFile(String fileName) {
        removeFile(ossProperties.getBucketName(), fileName);
    }

    @SneakyThrows
    @Override
    public void removeFile(String bucketName, String fileName) {
        client.removeObject(getBucketName(bucketName), fileName);
    }

    @Override
    public void removeFiles(List<String> fileNames) {
        removeFiles(ossProperties.getBucketName(), fileNames);
    }

    @Override
    @SneakyThrows
    public void removeFiles(String bucketName, List<String> fileNames) {
        //client.removeObjects(getBucketName(bucketName), fileNames);
        for (Result<DeleteError> errorResult: client.removeObjects(getBucketName(bucketName), fileNames)) {
            DeleteError error = errorResult.get();
            System.out.println("Failed to remove '" + error.objectName() + "'. Error:" + error.message());
        }
    }

    @Override
    public void downloadFile(HttpServletRequest request, HttpServletResponse response, String originalFilename, String fileName) {

        OutputStream outputStream = null;
        InputStream inputStream = null;
        try {
            outputStream = response.getOutputStream();
            if (StringUtils.isBlank(fileName)) {
                response.setHeader("Content-type", "text/html;charset=UTF-8");
                String data = "文件下载失败";
                outputStream.write(data.getBytes("UTF-8"));
                return;
            }

            if(StringUtils.isBlank(originalFilename)) {
                originalFilename = fileName.substring(fileName.lastIndexOf("/") + 1);
            }
            inputStream = getObject(fileName);
            OssFile ossFile = statFile(fileName);
            byte[] buf = new byte[1024];
            int length = 0;
            response.reset();
            //response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(originalFilename, "UTF-8"));
            response.setHeader("Content-Disposition", URLEncoder.encode(originalFilename, "UTF-8"));
            //response.setContentType("application/octet-stream");
            response.setContentType(ossFile.getContentType());
            response.setCharacterEncoding("UTF-8");
            while ((length = inputStream.read(buf)) > 0) {
                outputStream.write(buf, 0, length);
            }
            outputStream.close();
        } catch (IOException e) {
            try {
                response.setHeader("Content-type", "text/html;charset=UTF-8");
                String data = "文件下载失败";
                outputStream.write(data.getBytes("UTF-8"));
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 根据规则生成存储桶名称规则
     * @return
     */
    private String getBucketName() {
        return getBucketName(ossProperties.getBucketName());
    }

    /**
     * 根据规则生成存储桶名称规则
     * @param bucketName  存储桶名称
     * @return
     */
    private String getBucketName(String bucketName) {
        return ossRule.bucketName(bucketName);
    }

    /**
     * 根据规则生成存储桶文件名规则
     * @param originalFilename 原始文件名
     * @return
     */
    private String getFileName(String originalFilename) {
        return ossRule.fileName(originalFilename);
    }

    /**
     * 获取存储桶策略
     * @param policyType 策略枚举
     * @return
     */
    public String getPolicyType(PolicyType policyType){
        return getPolicyType(getBucketName(),policyType);
    }

    /**
     * 获取存储桶策略
     * @param bucketName 存储桶名称
     * @param policyType 策略枚举
     * @return
     */
    public static String getPolicyType(String bucketName, PolicyType policyType) {
        StringBuilder builder = new StringBuilder();
        builder.append("{\n");
        builder.append("    \"Statement\": [\n");
        builder.append("        {\n");
        builder.append("            \"Action\": [\n");

        switch (policyType) {
            case WRITE:
                builder.append("                \"s3:GetBucketLocation\",\n");
                builder.append("                \"s3:ListBucketMultipartUploads\"\n");
                break;
            case READ_WRITE:
                builder.append("                \"s3:GetBucketLocation\",\n");
                builder.append("                \"s3:ListBucket\",\n");
                builder.append("                \"s3:ListBucketMultipartUploads\"\n");
                break;
            default:
                builder.append("                \"s3:GetBucketLocation\"\n");
                break;
        }

        builder.append("            ],\n");
        builder.append("            \"Effect\": \"Allow\",\n");
        builder.append("            \"Principal\": \"*\",\n");
        builder.append("            \"Resource\": \"arn:aws:s3:::");
        builder.append(bucketName);
        builder.append("\"\n");
        builder.append("        },\n");
        if (PolicyType.READ.equals(policyType)) {
            builder.append("        {\n");
            builder.append("            \"Action\": [\n");
            builder.append("                \"s3:ListBucket\"\n");
            builder.append("            ],\n");
            builder.append("            \"Effect\": \"Deny\",\n");
            builder.append("            \"Principal\": \"*\",\n");
            builder.append("            \"Resource\": \"arn:aws:s3:::");
            builder.append(bucketName);
            builder.append("\"\n");
            builder.append("        },\n");

        }
        builder.append("        {\n");
        builder.append("            \"Action\": ");

        switch (policyType) {
            case WRITE:
                builder.append("[\n");
                builder.append("                \"s3:AbortMultipartUpload\",\n");
                builder.append("                \"s3:DeleteObject\",\n");
                builder.append("                \"s3:ListMultipartUploadParts\",\n");
                builder.append("                \"s3:PutObject\"\n");
                builder.append("            ],\n");
                break;
            case READ_WRITE:
                builder.append("[\n");
                builder.append("                \"s3:AbortMultipartUpload\",\n");
                builder.append("                \"s3:DeleteObject\",\n");
                builder.append("                \"s3:GetObject\",\n");
                builder.append("                \"s3:ListMultipartUploadParts\",\n");
                builder.append("                \"s3:PutObject\"\n");
                builder.append("            ],\n");
                break;
            default:
                builder.append("\"s3:GetObject\",\n");
                break;
        }

        builder.append("            \"Effect\": \"Allow\",\n");
        builder.append("            \"Principal\": \"*\",\n");
        builder.append("            \"Resource\": \"arn:aws:s3:::");
        builder.append(bucketName);
        builder.append("/*\"\n");
        builder.append("        }\n");
        builder.append("    ],\n");
        builder.append("    \"Version\": \"2012-10-17\"\n");
        builder.append("}\n");
        return builder.toString();
    }

    /**
     * 获取存储桶到域名
     * @param bucketName 存取桶名称
     * @return
     */
    public String getOssHost(String bucketName) {
        return ossProperties.getEndpoint().concat(StringPool.SLASH).concat(bucketName);
    }

    /**
     * 获取文件流
     * @param objectName w
     * @return
     */
    public InputStream getObject(String objectName) {
        return this.getObject(ossProperties.getBucketName(), objectName);
    }

    /**
     * 获取文件流
     * @param objectName w
     * @return
     */
    @SneakyThrows
    public InputStream getObject(String bucketName, String objectName) {
        return client.getObject(bucketName, objectName);
    }

    @Override
    public String getImageBase64(String objectName, String formatName) {
        return Base64Constant.BASE64_CAPTURE + ImageUtil.imageToBase64(this.getObject(objectName), formatName);
    }

    @Override
    public String getImageBase64(String objectName) {
        return getImageBase64(objectName, Base64Constant.BASE64_FORMAT_PNG);
    }

    @Override
    public String getImageBase64(String bucketName, String objectName, String formatName) {
        return Base64Constant.BASE64_CAPTURE + ImageUtil.imageToBase64(this.getObject(objectName), formatName);
    }






    /*public static void main(String[] args) {
        String fileUrl = "http://10.100.100.12:9000/salt-visual/upload/20201110/b176c1b9fdfdae6a81d610792da9e8a7.png";
        String url = fileUrl.split( "salt-visual/")[1];
        String str = url.substring(url.lastIndexOf("/") + 1);
        String str1 = url.substring(url.lastIndexOf("."));
        String dd = "b176c1b9fdfdae6a81d610792da9e8a7.png";
        System.out.println("url: " + url + "  ,str: " + str + "  ,str1: " + str1);
    }*/

}
